<!-- 3d 楼层 -->
<template>
  <div class="layer-3d container">
    <!-- 3d canvas  @click="clickBox"-->
    <div class="canvas-box" ref="container">
      <div
        class="unupload-tip"
        v-if="loadDataFinish && !currentLayer.three_dimensional_file"
      >
        您还没有为此楼层上传3D可视化模型，请前去菜单（楼宇-> 楼宇列表 ->
        楼层管理） 内编辑楼层上传fbx格式文件~
      </div>
    </div>
    <!-- 时间-->
    <current-time />
    <!-- 右侧操作栏 -->
    <option-bar @btnClick="handleOptBtnClick" />
    <!-- 切换楼宇按钮 -->
    <switch-building />
    <!-- 切换楼层 -->
    <switch-layer :list="layerList" />
    <!-- 统计图表信息 -->
    <div class="chart-info box1 hide-scroller-bar">
      <div class="page-title">
        <span v-if="currentInfo && currentInfo.build">
          {{ currentInfo.build.build_name }}
          -
        </span>
        {{ currentLayer.layer_name }}
      </div>
      <!-- 楼层信息 -->
      <div class="chart-module" v-if="Object.keys(layerAdmin).length">
        <div class="module-title">
          <cu-icon type="org-guanliyuan"></cu-icon>
          <span class="title">楼层管理员</span>
        </div>
        <div class="module-content">
          <a-row class="module-list-item">
            <a-col :span="6">姓名：</a-col>
            <a-col :span="18" class="text-white">
              {{ layerAdmin.realname }}
              <span v-if="layerAdmin.job_number"
                >（{{ layerAdmin.job_number }}）</span
              >
            </a-col>
          </a-row>
          <a-row class="module-list-item">
            <a-col :span="6">手机号：</a-col>
            <a-col :span="18" class="text-white">{{ layerAdmin.phone }}</a-col>
          </a-row>
        </div>
      </div>
      <!-- 房源状态 -->
      <div class="chart-module" v-if="false">
        <div class="module-title">
          <cu-icon type="org-title_3d"></cu-icon>
          <span class="title">房源状态</span>
        </div>
        <div class="module-content">
          <a-row :gutter="[12, 12]" :wrap="true">
            <a-col
              :span="8"
              v-for="(item, index) of areaData"
              :key="'area_' + index"
              class="area-item"
            >
              <div class="area-title">{{ item.title }}</div>
              <div class="area-num">
                <span class="num">{{ item.value }}</span>
                <span>{{ item.unit }}</span>
              </div>
            </a-col>
          </a-row>
          <div class="flex mt-20">
            <!-- 房源状态分布 -->
            <div id="housingStatus"></div>
            <div class="rate-box">
              <!-- 出租率 -->
              <div id="rentRate"></div>
              <!-- 到期率 -->
              <div id="expireRate"></div>
            </div>
          </div>
        </div>
      </div>
      <!-- 房源户型分布 -->
      <div class="chart-module" v-if="false">
        <div class="module-title">
          <cu-icon type="org-huxingfenbu"></cu-icon>
          <span class="title">房源户型分布</span>
        </div>
        <div class="module-content">
          <div id="housingType"></div>
        </div>
      </div>
      <!-- 在租面积 -->
      <div class="chart-module" v-if="false">
        <div class="module-title">
          <cu-icon type="org-mianjipaihang"></cu-icon>
          <span class="title">在租面积统计</span>
        </div>
        <div class="module-content">
          <a-row :gutter="[12, 12]" :wrap="true">
            <a-col
              :span="8"
              v-for="(item, index) of areaRate"
              :key="'rate_' + index"
              class="area-item"
            >
              <div class="area-title">{{ item.title }}</div>
              <div class="area-num">
                <span class="num">{{ item.value }}</span>
                <span>%</span>
              </div>
            </a-col>
          </a-row>
          <div id="areaRank"></div>
          <!-- <a-row class="rent-title text-main">
            <a-col :span="16">租户名称</a-col>
            <a-col :span="8">在租面积（㎡）</a-col>
          </a-row>
          <a-row
            class="rent-item"
            v-for="(item, index) of rentList"
            :key="index"
          >
            <a-col :span="16">{{ item.name }}</a-col>
            <a-col :span="8">{{ item.value }}</a-col>
          </a-row> -->
        </div>
      </div>

      <!-- 房间信息 -->
      <div class="room-info box2" v-if="showRoomInfo">
        <a-row class="box2-header" type="flex" align="middle">
          <a-col flex="auto" class="room-title">
            <span>
              {{ currentRoomInfo.room_name }}
            </span>
            <a-tooltip v-if="type == 'edit'">
              <template #title>解除当前模型区块与房间号的绑定关系</template>
              <a-button class="ml-10" ghost danger @click="unbindCurrentRoom"
                >解绑</a-button
              >
            </a-tooltip>
          </a-col>
          <a-col flex="40px" class="close" @click="handleRoomInfoHide">
            <CloseOutlined />
          </a-col>
        </a-row>
        <div class="room-content">
          <a-row :gutter="[12, 6]" :wrap="true">
            <a-col :span="12" class="area-item">
              <div class="area-title">
                <cu-icon class="icon" type="org-jianzhumianji" />
                建筑面积
              </div>
              <div class="area-num">
                <span class="num">{{
                  currentRoomInfo.build_area || "--"
                }}</span>
                <span>㎡</span>
              </div>
            </a-col>
            <a-col :span="12" class="area-item">
              <div class="area-title">
                <cu-icon class="icon" type="org-jizumianji" />
                计租面积
              </div>
              <div class="area-num">
                <span class="num">{{
                  currentRoomInfo.rental_area || "--"
                }}</span>
                <span>㎡</span>
              </div>
            </a-col>
            <a-col :span="12" class="area-item">
              <div class="area-title">
                <cu-icon class="icon" type="org-zhuangxiu" />
                装修状况
              </div>
              <div class="area-num">
                <span class="num">{{
                  currentRoomInfo.decoration_txt || "--"
                }}</span>
              </div>
            </a-col>
            <a-col :span="12" class="area-item">
              <div class="area-title">
                <cu-icon class="icon" type="org-baojia" />
                报价
              </div>
              <div class="area-num">
                <span class="num">{{
                  currentRoomInfo.pre_unit_price || "--"
                }}</span>
                <span>{{ currentRoomInfo.price_unit_txt }}</span>
              </div>
            </a-col>
          </a-row>
          <a-row
            class="room-status"
            :style="{
              backgroundColor: layerFbx[currentObjId].defaultColor,
            }"
            @click="handleRoomStatusClick"
          >
            <a-col :span="24">{{ currentRoomInfo.room_status_txt }}</a-col>
          </a-row>
          <a-row
            :wrap="true"
            :gutter="[12, 12]"
            class="renter-info"
            v-if="currentRoomInfo.owner"
          >
            <a-col :span="6" class="name">租客：</a-col>
            <a-col :span="18" class="info">{{
              currentRoomInfo.owner.ownerData.name
            }}</a-col>
            <a-col :span="6" class="name">租期：</a-col>
            <a-col :span="18" class="info">{{
              currentRoomInfo.owner.leaseStartTime +
              " ~ " +
              currentRoomInfo.owner.leaseEndTime
            }}</a-col>
            <a-col :span="6" class="name">联系人：</a-col>
            <a-col :span="18" class="info"
              >{{ currentRoomInfo.owner.ownerData.contact_name }}
              <span
                v-if="
                  currentRoomInfo.owner.ownerData.owner_contact &&
                  currentRoomInfo.owner.ownerData.owner_contact.phone
                "
              >
                - {{ currentRoomInfo.owner.ownerData.owner_contact.phone }}
              </span>
            </a-col>
          </a-row>
          <!-- <a-row class="room-status unbind" @click="unbindCurrentRoom">
            <a-col :span="24">解绑当前房号</a-col>
          </a-row> -->
        </div>
      </div>
    </div>
    <a-modal
      v-model:visible="bindRoomVisible"
      title="绑定房间"
      @ok="handleRoomBind"
      centered
    >
      <a-alert
        message="该区域还未绑定房间，您可以选择当前楼层下还未绑定过的房间名称进行绑定; 若需要更改绑定关系您需要先解绑再选择绑定。"
        type="info"
        show-icon
      />
      <a-select
        v-model:value="bindRoomId"
        class="w-100p mt-20"
        placeholder="请选择房间"
        show-search
        :filterOption="filterOption"
        :options="
          allRoomList.map((item) => ({
            value: item.id,
            label: item.room_name,
          }))
        "
      >
      </a-select>
    </a-modal>
  </div>
</template>

<script>
import {
  computed,
  defineComponent,
  nextTick,
  onMounted,
  reactive,
  toRaw,
  toRefs,
  watch,
} from "vue";
import { CloseOutlined } from "@ant-design/icons-vue";
import CurrentTime from "./modules/CurrentTime.vue";
import OptionBar from "./modules/OptionBar.vue";
import SwitchBuilding from "./modules/SwitchBuilding.vue";
import SwitchLayer from "./modules/SwitchLayer.vue";
import { Chart } from "@antv/g2";
import * as THREE from "three";
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; //控制器
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass";
import { useRoute, useRouter } from "vue-router";
import request from "../../common/utils/request";
import store from "../../store";
import { message } from "ant-design-vue";
import { Color } from "@antv/attr";
import utils from "../../common/utils/utils";

const unwarp = (obj) => obj && (obj.__v_raw || obj.valueOf() || obj);
let composer = null;
let outlinePass = null;
let renderPass = null;
let effectFXAA = new ShaderPass(FXAAShader);

export default defineComponent({
  name: "Layer3D",
  components: {
    CurrentTime,
    OptionBar,
    SwitchBuilding,
    SwitchLayer,
    CloseOutlined,
  },
  setup() {
    const $route = useRoute();
    const state = reactive({
      areaData: [],
      areaRate: [],
      rentList: [],
      showRoomInfo: false,
      currentRoomInfo: {},
      mouseDownPosition: {},
      lastIntersect: null,
      layerId: $route.query.layerId,
      buildId: $route.query.buildId,
      villageId: $route.query.villageId,
      type: $route.query.type, // 预览模式还是编辑模式
      layerList: [],
      currentLayer: {},
      // 绑定房间
      bindRoomVisible: false,
      bindRoomId: undefined,
      currentObjId: 0,
      allRoomList: [],
      // 房间与3d图的映射关系
      roomLayerRelation: {},
      // 3d图id映射
      layerFbx: {},
      load3DFinish: false,
      loadDataFinish: false,
      // 状态颜色值
      // statusColor: []
      // 楼层管理员
      layerAdmin: {},
    });
    const filterOption = (input, option) => {
      return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
    };
    const layerCharts = reactive({
      housingStatus: null,
      rentRate: null,
      expireRate: null,
      housingType: null,
      areaRank: null,
    });
    const currentInfo = computed(() => store.getters.layerCurrentInfo);
    // 初始化房源状态分布图表
    const initHousingStatus = () => {
      const data = [
        { color: "#a9dcfd", title: "空置中", num: 10, percent: 0.1 },
        { color: "#ce9ced", title: "已锁定", num: 10, percent: 0.1 },
        { color: "#c6e0a7", title: "已出租", num: 35, percent: 0.2 },
        { color: "#fecb85", title: "90日内到期", num: 10, percent: 0.1 },
        { color: "#fdac93", title: "30日内到期", num: 30, percent: 0.3 },
        { color: "#ff7875", title: "已到期", num: 20, percent: 0.2 },
      ];
      const colorList = data.map((item) => item.color);
      layerCharts.housingStatus = new Chart({
        container: "housingStatus",
        autoFit: true,
        height: 150,
        localRefresh: false,
      });
      layerCharts.housingStatus.legend(false);
      layerCharts.housingStatus.data(data);
      layerCharts.housingStatus.tooltip({
        showTitle: false,
        showMarkers: false,
      });
      layerCharts.housingStatus.axis("title", false);
      layerCharts.housingStatus.axis("num", {
        label: {
          style: {
            fill: "rgba(255,255,255, 0.8)",
          },
        },
      });
      layerCharts.housingStatus
        .interval()
        .position("title*num")
        .color("title", colorList);
      // .style({
      //   fillOpacity: 0.8,
      // });
      layerCharts.housingStatus
        .interval()
        .position("title*num")
        .color("title", colorList)
        .size(40)
        .shape("tick");
      layerCharts.housingStatus.interaction("active-region");
      layerCharts.housingStatus.render();
    };

    // 初始化房间出租率图表
    const initRentRate = () => {
      const data = [{ type: "出租率", value: 56.4 }];
      layerCharts.rentRate = new Chart({
        container: "rentRate",
        // autoFit: true,
        width: 120,
        height: 90,
      });
      layerCharts.rentRate.data(data);
      layerCharts.rentRate.legend(false);
      layerCharts.rentRate.tooltip(false);
      layerCharts.rentRate.facet("rect", {
        fields: ["type"],
        padding: 0,
        showTitle: false,
        eachView: (view, facet) => {
          const data = facet.data;
          data.push({ type: "未出租率", value: 100 - data[0].value });
          view.data(data);
          view.coordinate("theta", {
            radius: 0.9,
            innerRadius: 0.8,
          });
          view
            .interval()
            .adjust("stack")
            .position("value")
            .color("type", ["#01c3fd", "#eceef1"])
            .style({
              opacity: 1,
            });
          view.annotation().text({
            position: ["50%", "50%"],
            content: data[0].type,
            style: {
              fontSize: 12,
              fill: "#fff",
              textBaseline: "bottom",
              textAlign: "center",
            },
            offsetY: -10,
          });

          view.annotation().text({
            position: ["50%", "50%"],
            content: data[0].value + "%",
            style: {
              fontSize: 18,
              fill: "#fff",
              fontWeight: 500,
              textAlign: "center",
            },
            offsetY: 10,
          });

          view.interaction("element-active");
        },
      });
      layerCharts.rentRate.render();
    };

    // 初始化房间逾期率图表
    const initExpireRate = () => {
      const data = [{ type: "逾期率", value: 22.4 }];
      layerCharts.expireRate = new Chart({
        container: "expireRate",
        // autoFit: true,
        width: 120,
        height: 90,
      });
      layerCharts.expireRate.data(data);
      layerCharts.expireRate.legend(false);
      layerCharts.expireRate.tooltip(false);
      layerCharts.expireRate.facet("rect", {
        fields: ["type"],
        padding: 0,
        showTitle: false,
        eachView: (view, facet) => {
          const data = facet.data;
          data.push({ type: "未逾期率", value: 100 - data[0].value });
          view.data(data);
          view.coordinate("theta", {
            radius: 0.9,
            innerRadius: 0.8,
          });
          view
            .interval()
            .adjust("stack")
            .position("value")
            .color("type", ["#ff0000", "#eceef1"])
            .style({
              opacity: 1,
            });
          view.annotation().text({
            position: ["50%", "50%"],
            content: data[0].type,
            style: {
              fontSize: 12,
              fill: "#fff",
              textBaseline: "bottom",
              textAlign: "center",
            },
            offsetY: -10,
          });

          view.annotation().text({
            position: ["50%", "50%"],
            content: data[0].value + "%",
            style: {
              fontSize: 18,
              fill: "#fff",
              fontWeight: 500,
              textAlign: "center",
            },
            offsetY: 10,
          });

          view.interaction("element-active");
        },
      });
      layerCharts.expireRate.render();
    };

    // 初始化楼层户型分布图表
    const initHousingType = () => {
      const data = [
        { type: "0~100", value: 27 },
        { type: "100~300", value: 25 },
        { type: "300~600", value: 18 },
        { type: "600~1000", value: 15 },
        { type: "10000以上", value: 5 },
      ];

      layerCharts.housingType = new Chart({
        container: "housingType",
        autoFit: true,
        height: 160,
        // padding: [40, 40, 0, 0],
      });

      layerCharts.housingType.data(data);
      layerCharts.housingType.coordinate("polar", {
        startAngle: Math.PI, // 起始角度
        endAngle: Math.PI * (3 / 2), // 结束角度
      });
      layerCharts.housingType.legend({
        position: "right",
        itemName: {
          style: {
            fill: "#fff",
          },
        },
      });

      layerCharts.housingType.scale("value", {
        tickCount: 6,
      });

      layerCharts.housingType.axis("value", {
        grid: {
          line: {
            type: "circle",
          },
          closed: false,
        },
        verticalFactor: 1,
        label: {
          offset: 15,
          style: {
            fill: "#fff",
          },
        },
      });

      layerCharts.housingType.axis("type", {
        tickLine: {
          alignTick: false,
        },
        grid: {
          alignTick: false,
        },
        label: {
          style: {
            fill: "#fff",
          },
        },
      });

      layerCharts.housingType.tooltip({
        showMarkers: false,
      });
      layerCharts.housingType.interaction("element-highlight");

      layerCharts.housingType
        .interval()
        .position("type*value")
        .color("type", "rgb(24, 159, 190)-rgb(1, 75, 163)")
        .label("value", {
          offset: -15,
          style: {
            textAlign: "center",
            fill: "#fff",
          },
        })
        .style({
          lineWidth: 1,
          stroke: "#fff",
        });

      layerCharts.housingType.render();
    };

    const initAreaRank = () => {
      const data = [
        { name: "u somehow", value: 110 },
        { name: "妞妞女士", value: 778 },
        { name: "我家银子", value: 1109 },
        { name: "小猪科技", value: 3679 },
        { name: "快鲸", value: 4000 },
      ];
      layerCharts.areaRank = new Chart({
        container: "areaRank",
        autoFit: true,
        height: 200,
      });
      layerCharts.areaRank.data(data);
      layerCharts.areaRank.scale({
        value: {
          max: 10000,
          min: 0,
          alias: "在租面积（㎡）", //
        },
      });
      layerCharts.areaRank.axis("name", {
        title: null,
        tickLine: null,
        line: null,
        label: {
          style: {
            fill: "#fff",
          },
        },
      });

      layerCharts.areaRank.axis("value", {
        label: null,
        title: {
          offset: 30,
          style: {
            fontSize: 12,
            fill: "#fff",
          },
        },
      });
      layerCharts.areaRank.legend(false);
      layerCharts.areaRank.coordinate().transpose();
      layerCharts.areaRank
        .interval()
        .position("name*value")
        .color("value", "rgb(24, 159, 190)-rgb(1, 75, 163)")
        // .size(26)
        .label("value", {
          style: {
            fill: "#fff",
          },
          offset: 10,
        });
      layerCharts.areaRank.interaction("element-active");
      layerCharts.areaRank.render();
    };

    const initAreaData = () => {
      state.areaData = [
        { title: "在租面积", value: 1989.6, unit: "㎡" },
        { title: "管理面积", value: 765.8, unit: "㎡" },
        { title: "空置招商面积", value: 782.1, unit: "㎡" },
        // { title: "在租实时均价", value: 9, unit: "元/m²·月" },
      ];
      state.areaRate = [
        { title: "互联网", value: 52 },
        { title: "餐饮", value: 26 },
        { title: "金融", value: 18 },
      ];
      // state.rentList = [
      //   { name: "安徽小猪科技", value: 1109 },
      //   { name: "我家银子不听话", value: 3679 },
      //   { name: "妞妞女士", value: 778 },
      //   { name: "u somehow", value: 110 },
      // ];
    };

    // 3d模型对象
    const layerModel = reactive({
      container: null,
      scene: {}, // 场景对象
      renderer: {}, // 渲染器对象
      camera: {}, // 相机对象
      axesHelper: {}, // 三维坐标系对象
      stats: {}, // 性能监控对象
      light: {
        spotLight: {}, // 光源对象
        HemisphereLight: {}, // 环境光
      }, // 光源集
      controls: {}, // 辅助对象
      clock: {}, // 时钟工具对象
      trackballControls: {}, // 控制器对象
    });
    // 初始化场景对象
    const initScene = () => {
      layerModel.scene = new THREE.Scene();
      // 创建三维坐标系
      layerModel.axesHelper = new THREE.AxesHelper(0);
      layerModel.scene.add(layerModel.axesHelper);
    };

    // 初始化光源
    const initLight = () => {
      // layerModel.light.spotLight = new THREE.SpotLight(0xffffff);
      // layerModel.light.spotLight.position.set(0, 100, 0);
      // layerModel.light.spotLight.castShadow = false;
      // layerModel.scene.add(layerModel.light.spotLight);
      layerModel.light.hemiLight = new THREE.HemisphereLight(
        0xffffff,
        0x000000,
        1
      );
      layerModel.light.hemiLight.position.set(0, 100, 0); //这个也是默认位置
      layerModel.scene.add(layerModel.light.hemiLight);
    };

    // 初始化相机
    const initCamera = () => {
      layerModel.camera = new THREE.PerspectiveCamera(
        45,
        layerModel.container.offsetWidth / layerModel.container.offsetHeight,
        0.1,
        1000
      );
      // 相机位置
      layerModel.camera.position.set(120, 120, 120);
      // 设置相机指向的位置 默认0，0，0
      layerModel.camera.lookAt(layerModel.scene.position);
    };

    // 渲染器对象
    const initRenderer = () => {
      layerModel.renderer = new THREE.WebGLRenderer({
        antialias: true,
      }); // antialias（是否启用抗锯齿）
      layerModel.renderer.setSize(
        layerModel.container.offsetWidth,
        layerModel.container.offsetHeight
      );

      // 告诉渲染器需要阴影效果
      layerModel.renderer.shadowMap.enabled = true;
      layerModel.renderer.shadowMap.type = THREE.PCFSoftShadowMap;

      layerModel.renderer.setClearColor(0xffffff, 0.1);
      // layerModel.renderer.setClearColor(0xffffff);
      // layerModel.stats = new ThreeStats.Stats();
      layerModel.container.appendChild(layerModel.renderer.domElement);
    };

    // 控制器
    const initControls = () => {
      layerModel.controls = new OrbitControls(
        layerModel.camera,
        layerModel.renderer.domElement
      ); //创建控件对象
      // layerModel.controls.addEventListener("change", animation);
    };

    // 加载模型
    const initLoader = () => {
      const loader = new FBXLoader();
      state.load3DFinish = false;
      loader.load(state.currentLayer.three_dimensional_file, (fbx) => {
        console.log("initLoader", fbx);
        fbx.traverse((child) => {
          if (child instanceof THREE.Mesh) {
            state.layerFbx[child.ID] = child;
          }
        });
        state.load3DFinish = true;
        getScale(fbx);
        layerModel.scene.add(fbx);
      });
    };

    watch(
      [() => state.loadDataFinish, () => state.load3DFinish],
      ([finish1, finish2]) => {
        if (finish1 && finish2) {
          setFbxColor();
        }
      }
    );
    const setFbxColor = () => {
      const { layerFbx, roomLayerRelation } = state;
      console.log("roomLayerRelation", toRaw(roomLayerRelation));
      /**
       * originColor 模型原始材料颜色
       * defaultMColor 模型默认材料颜色（与绑定房间的状态有关）
       * defaultColor 模型默认材料颜色的十六进制
       */
      Object.keys(layerFbx).forEach((objId) => {
        // 克隆原始材料
        const cloneObj = layerFbx[objId].material.clone();
        // 记录默认渲染颜色
        if (layerFbx[objId].originColor) {
          layerFbx[objId].defaultMColor = layerFbx[objId].originColor;
        } else {
          // 第一次才记录原始材料颜色
          layerFbx[objId].originColor = layerFbx[objId].defaultMColor =
            cloneObj.color;
        }
        if (roomLayerRelation[objId]) {
          // 已绑定过房间
          const color = roomLayerRelation[objId].roomColor;
          if (color) {
            const objColor = parseInt(color.slice(1), 16);
            cloneObj.color.set(objColor);
            layerFbx[objId].defaultMColor = objColor;
            layerFbx[objId].defaultColor = color;
          }
        } else {
          cloneObj.color.set(layerFbx[objId].originColor);
        }
        layerFbx[objId].material = cloneObj;
      });
    };
    const getScale = (fbx) => {
      const box3 = new THREE.Box3();
      box3.expandByObject(fbx); // 计算层级模型包围盒
      const v3 = new THREE.Vector3();
      // 获得包围盒长宽高尺寸，结果保存在参数三维向量对象v3中
      box3.getSize(v3);
      const S = 150 / num(v3);
      // 对模型进行缩放操作，实现全屏效果
      fbx.scale.set(S, S, S);
      const newBox3 = new THREE.Box3();
      newBox3.expandByObject(fbx);
      const center = new THREE.Vector3(); // 计算一个层级模型对应包围盒的几何体中心
      newBox3.getCenter(center);
      fbx.position.x = fbx.position.x - center.x;
      fbx.position.y = fbx.position.y - center.y + 20;
      fbx.position.z = fbx.position.z - center.z;
    };

    // 执行渲染操作
    const animation = () => {
      requestAnimationFrame(animation);
      unwarp(layerModel.renderer).render(
        unwarp(layerModel.scene),
        layerModel.camera
      );
      if (composer) {
        composer.render();
      }
    };

    const onResize = () => {
      //更新镜头长宽比
      layerModel.camera.aspect =
        layerModel.container.offsetWidth / layerModel.container.offsetHeight;

      //更新摄像机投影矩阵。在任何参数被改变以后必须被调用。
      layerModel.camera.updateProjectionMatrix();

      //设置渲染器宽长
      layerModel.renderer.setSize(
        layerModel.container.offsetWidth,
        layerModel.container.offsetHeight
      );
      if (composer) {
        composer.setSize(
          layerModel.container.offsetWidth,
          layerModel.container.offsetHeight
        );
      }
    };

    // 鼠标按下
    const onMouseDown = (event) => {
      state.mouseDownPosition = {
        clientX: event.clientX,
        clientY: event.clientY,
      };
    };
    // 鼠标拖动
    // const onMouseMove = (event) => {};
    // 鼠标按起
    const onMouseUp = (event) => {
      const { mouseDownPosition } = state;
      if (
        event.clientX === mouseDownPosition.clientX &&
        event.clientY === mouseDownPosition.clientY
      ) {
        // 点击事件
        const raycaster = new THREE.Raycaster();
        const mouse = new THREE.Vector2();
        //将鼠标点击位置的屏幕坐标转换成threejs中的标准坐标
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = 1 - (event.clientY / window.innerHeight) * 2;
        // // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
        raycaster.setFromCamera(mouse, layerModel.camera);

        // 获取raycaster直线和所有模型相交的数组集合
        const intersects = raycaster.intersectObjects(
          layerModel.scene.children
        );

        //将相交的模型的颜色设置为高亮
        if (intersects && intersects.length) {
          // 当前选中的模型
          const currentObj = intersects[0].object;
          state.currentObjId = currentObj.ID;
          // 上一个选中的模型
          let lastObjId = 0;
          if (state.lastIntersect) {
            lastObjId = state.lastIntersect.object.ID;
          }
          const cloneObj = currentObj.material.clone();
          if (state.currentObjId != lastObjId) {
            // 当前高亮
            cancelOutlineObj();
            cloneObj.color.set(0x45f1fb);
            // outlineObj(currentObj);
            state.lastIntersect = intersects[0];
          }

          currentObj.material = cloneObj;
          // 已经绑定过房间就直接显示房间信息；没有绑定房间的需要去绑定（编辑模式下）
          if (state.roomLayerRelation[state.currentObjId]) {
            // 已经绑定过房间就直接显示房间信息
            // if (composer) {
            //   handleRoomInfoShow();
            // }
            handleRoomInfoShow();
          } else {
            if (state.type == "edit") {
              // 没有绑定房间的需要去绑定（编辑模式下）
              state.bindRoomVisible = true;
            } else {
              message.info("当前模块未关联绑定房间号！");
            }
          }
        }
      } else {
        state.mouseDownPosition = {};
      }
    };
    watch(
      () => state.bindRoomVisible,
      (val) => {
        if (val) {
          getAllRoomList();
        }
      }
    );

    const handleRoomInfoShow = () => {
      const roomId = state.roomLayerRelation[state.currentObjId].id;
      request
        .get("/village", "/project/room/get", {
          id: roomId,
          village_id: state.villageId,
          three_dimensional_id: state.currentObjId,
        })
        .then((res) => {
          state.currentRoomInfo = res.detail;
          if (res.detail.room_status >= 20) {
            request
              .get("/org", "/room/getContract", {
                room_id: roomId,
              })
              .then((res) => {
                if (res.data && res.data.length) {
                  state.currentRoomInfo.owner = res.data[0];
                }
              });
          }
          state.showRoomInfo = true;
        });
    };

    const handleRoomInfoHide = () => {
      state.currentRoomInfo = {};
      cancelOutlineObj(true);
    };

    // 取消高亮和选中效果
    const cancelOutlineObj = (clearLastIntersect = false) => {
      if (state.lastIntersect) {
        const lastObjId = state.lastIntersect.object.ID;
        // 上一次选中的模型取消颜色选中
        state.lastIntersect.object.material.color.set(
          state.layerFbx[lastObjId].defaultMColor
        );
        if (clearLastIntersect) {
          state.lastIntersect = null;
        }
        composer = null;
        state.showRoomInfo = false;
      }
    };

    // 选中发光特效
    const outlineObj = (selectedObjects) => {
      // 创建效果组合器对象，在该对象上添加后期处理通道
      composer = new EffectComposer(layerModel.renderer);
      // 新建一个场景通道，覆盖到原来的场景上
      renderPass = new RenderPass(toRaw(layerModel.scene), layerModel.camera);
      composer.addPass(renderPass);
      // 物体边缘发光通道
      outlinePass = new OutlinePass(
        new THREE.Vector2(window.innerWidth, window.innerHeight),
        toRaw(layerModel.scene),
        layerModel.camera
      );
      // 发光的对象是个数组
      outlinePass.selectedObjects = [selectedObjects];
      // 边框亮度
      outlinePass.edgeStrength = 20.0;
      // 光晕[0,1]
      outlinePass.edgeGlow = 1;
      // 是否使用父级的材质
      outlinePass.usePatternTexture = false;
      // 边框宽度
      outlinePass.edgeThickness = 1.0;
      // 边框弯曲度
      outlinePass.downSampleRatio = 1;
      // 呼吸闪烁的速度
      outlinePass.pulsePeriod = 4;
      // 呼吸显示的颜色
      outlinePass.visibleEdgeColor.set(0x45f1fb);
      // outlinePass.visibleEdgeColor.set(selectedObjects.material.color);
      // 呼吸消失的颜色
      outlinePass.hiddenEdgeColor = new THREE.Color(255, 255, 255);

      outlinePass.clear = true;
      composer.addPass(outlinePass);

      // 自定义着色器通道
      effectFXAA.uniforms["resolution"].value.set(
        1 / window.innerWidth,
        1 / window.innerHeight
      );
      effectFXAA.renderToScreen = true;
      composer.addPass(effectFXAA);
    };

    // 全局事件监听
    const eventListener = () => {
      initControls();

      // 监听窗口尺寸变化
      window.addEventListener("resize", onResize, false);
      // 监听鼠标按下
      layerModel.container.addEventListener("mousedown", onMouseDown, false);
      //  监听鼠标移动
      //  layerModel.container.addEventListener("mousemove", onMouseMove, false);
      // 监听鼠标弹起
      layerModel.container.addEventListener("mouseup", onMouseUp, false);
    };

    const handleOptBtnClick = (e) => {
      console.log("handleOptBtnClick", e);
      const { type } = e;
      if (type == "overview") {
        // 总览 恢复起始视角
        initCamera();
        initControls();
        cancelOutlineObj();
      }
    };

    const getLayerList = () => {
      request
        .get("/village", "/project/layer/all", {
          build_id: state.buildId,
        })
        .then((res) => {
          if (res.list.length) {
            state.layerList = res.list;
            // 存入初始化信息
            if (!state.layerId) {
              state.layerId = res.list[0].id;
            }
            state.currentLayer = state.layerList.find(
              (item) => item.id == state.layerId
            );
            store.dispatch("SetCurrentInfo", { layer: state.currentLayer });
            if (state.currentLayer.three_dimensional_file) {
              // 有3d图
              get3DData();
              init3DLayer();
            } else {
              state.loadDataFinish = true;
            }
          }
        });
    };

    const handleRoomBind = () => {
      if (!state.bindRoomId) {
        message.warning("请选择房间！");
        return;
      }
      // 绑定
      request
        .put("/village", "/project/room/three_dimensional_id", {
          id: state.bindRoomId,
          village_id: state.villageId,
          three_dimensional_id: state.currentObjId,
          needMsg: true,
        })
        .then((res) => {
          state.bindRoomVisible = false;
          state.bindRoomId = undefined;
          message.success(res.msg || "绑定成功！");
          get3DData();
        });
    };
    // 解绑
    const unbindCurrentRoom = () => {
      utils.modalConfirm({
        content: `确认要将此模块与房间“${state.currentRoomInfo.room_name}”解绑吗？`,
        centered: true,
        okType: "danger",
        onOk: () => {
          request
            .put("/village", "/project/room/three_dimensional_id", {
              id: state.currentRoomInfo.id,
              village_id: state.villageId,
              three_dimensional_id: "",
              needMsg: true,
            })
            .then((res) => {
              message.success(res.msg || "解绑成功！");
              state.showRoomInfo = false;
              handleRoomInfoHide();
              get3DData();
            });
        },
      });
    };

    const getAllRoomList = () => {
      if (state.type === "edit") {
        request
          .get("/village", "/project/room/all", {
            village_id: state.villageId,
            layer_id: state.layerId,
            not_three_dimensional: 1,
          })
          .then((res) => {
            state.allRoomList = res.list;
          });
      }
    };

    const handleRoomStatusClick = () => {
      const { owner } = state.currentRoomInfo;
      if (owner && owner.id) {
        window.open(
          `${location.origin}/orgs/#/apps/orgContract/contractDetail?id=${owner.id}&from=mobile`
        );
      }
    };

    const get3DData = () => {
      state.loadDataFinish = false;
      state.roomLayerRelation = {};
      request
        .get("/village", "/profile/profile", {
          villageId: state.villageId,
          buildId: state.buildId,
        })
        .then((res) => {
          const { list } = res;
          const layer = list.find((item) => item.layer_id == state.layerId);
          if (layer.room_list && layer.room_list.length) {
            layer.room_list.forEach((item) => {
              if (item.three_dimensional_id) {
                // 绑定改过房间
                state.roomLayerRelation[item.three_dimensional_id] = item;
              }
            });
          }
          state.loadDataFinish = true;
        });
    };

    const getLayerAdmin = () => {
      request
        .get("/org", "/layers/admin", {
          layer_id: state.layerId,
        })
        .then((res) => {
          if (res.list.length) {
            state.layerAdmin = res.list[0];
          }
        });
    };

    const init3DLayer = () => {
      initScene();
      initLight();
      initCamera();
      initRenderer();
      initLoader();

      animation();
      nextTick(() => {
        eventListener();
      });
    };

    const initCharts = () => {
      initAreaData();
      nextTick(() => {
        initHousingStatus();
        initRentRate();
        initExpireRate();
        initHousingType();
        initAreaRank();
      });
    };
    onMounted(() => {
      // initCharts();
      // init3DLayer();
    });

    const init = () => {
      getLayerList();
      getLayerAdmin();
    };

    init();
    return {
      currentInfo,
      filterOption,
      ...toRefs(state),
      ...toRefs(layerModel),
      handleOptBtnClick,
      handleRoomBind,
      handleRoomInfoHide,
      unbindCurrentRoom,
      handleRoomStatusClick,
    };
  },
});
// 计算包围盒的最大边长
function num(v3) {
  let max;
  if (v3.x > v3.y) {
    max = v3.x;
  } else {
    max = v3.y;
  }
  if (max > v3.z) {
  } else {
    max = v3.z;
  }
  return max;
}
</script>
<style lang="less" scoped>
@import "./index.less";
#housingStatus {
  width: 200px;
  height: 180px;
}
.rate-box {
  margin-left: 10px;
  width: 120px;
  height: 180px;
  #rentRate {
    width: 100%;
    height: 90px;
  }
  #expireRate {
    width: 100%;
    height: 80px;
    margin-top: 5px;
  }
}
#housingType {
  width: 320px;
  height: 160px;
}
.area-item {
  line-height: 24px;
  font-size: 14px;
  .area-num {
    color: #fff;
    .num {
      font-size: 24px;
      margin-right: 4px;
      font-weight: bold;
    }
  }
}

#areaRank {
  width: 300px;
  height: 200px;
  margin-top: 20px;
}

.rent-title {
  padding: 5px 0;
  // color: #fff;
  font-size: 15px;
}

.rent-item {
  padding: 5px 0;
}

.room-info {
  position: fixed;
  right: 100px;
  top: 100px;
  width: 360px;
  // height: 400px;
  .room-title {
    font-size: 28px;
    font-weight: bold;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  .room-content {
    max-height: 500px;
    padding: 20px;
    .area-item {
      line-height: 30px;
      .area-title {
        font-size: 16px;
        display: flex;
        align-items: center;
        .icon {
          font-size: 18px;
          margin-right: 8px;
          color: @mainColor;
        }
      }
      .area-num {
        .num {
          font-size: 20px;
          margin-right: 10px;
        }
      }
    }
    .room-status {
      margin-top: 20px;
      height: 40px;
      line-height: 40px;
      text-align: center;
      // background: #c6e0a760;
      border-radius: 8px;
      font-size: 18px;
      color: #fff;
      letter-spacing: 2px;
      cursor: pointer;
    }
    .renter-info {
      margin-top: 20px;
      color: #fff;
      .name {
        font-size: 16px;
      }
      .info {
        color: @mainColor;
        font-weight: bold;
      }
    }
    .unbind {
      background: @error-color;
      cursor: pointer;
    }
  }
}
</style>
